/*
//
//              INTEL CORPORATION PROPRIETARY INFORMATION
//  This software is supplied under the terms of a license  agreement or
//  nondisclosure agreement with Intel Corporation and may not be copied
//  or disclosed except in  accordance  with the terms of that agreement.
//    Copyright (c) 2007-2008 Intel Corporation. All Rights Reserved.
//
//
*/

#include "umc_defs.h"
#if defined(UMC_ENABLE_AVS_VIDEO_ENCODER)

#include <trace_log.h>
#include "umc_avs_enc_bit_stream.h"
#include "umc_avs_sequence_header.h"
#include "umc_avs_picture_header.h"
#include "umc_avs_slice_header.h"

namespace UMC
{

void EncodePicHeader(AVS_BIT_STREAM_CONTEXT *pCtx,
                     const AVS_SEQUENCE_HEADER *pSeqHeader,
                     const AVS_PICTURE_HEADER *pPicHeader)
{
    WRITE_TRACE_LOG(_enc_hdr, "picture header", 0);

    // put the start code
    PutBits(pCtx, 1, 24);
    PutBits(pCtx, pPicHeader->picture_start_code, 8);
    WRITE_TRACE_LOG(_enc_hdr, "picture_start_code", pPicHeader->picture_start_code);

    // save information about I frame
    if (I_PICTURE_START_CODE == pPicHeader->picture_start_code)
    {
        PutBits(pCtx, pPicHeader->bbv_delay, 16);
        WRITE_TRACE_LOG(_enc_hdr, "bbv_delay", pPicHeader->bbv_delay);
        PutBit(pCtx, pPicHeader->time_code_flag);
        WRITE_TRACE_LOG(_enc_hdr, "time_code_flag", pPicHeader->time_code_flag);
        if (pPicHeader->time_code_flag)
        {
            PutBits(pCtx, pPicHeader->time_code, 24);
            WRITE_TRACE_LOG(_enc_hdr, "time_code", pPicHeader->time_code);
        }
        // NOTE: it is absent in standard, but
        // reference code has the following 1 bit skipping
        PutBit(pCtx, 1);
        PutBits(pCtx, pPicHeader->picture_distance, 8);
        WRITE_TRACE_LOG(_enc_hdr, "picture_distance", pPicHeader->picture_distance);
        if (pSeqHeader->low_delay)
        {
            PutUE(pCtx, pPicHeader->bbv_check_times);
            WRITE_TRACE_LOG(_enc_hdr, "bbv_check_times", pPicHeader->bbv_check_times);
        }
        PutBit(pCtx, pPicHeader->progressive_frame);
        WRITE_TRACE_LOG(_enc_hdr, "progressive_frame", pPicHeader->progressive_frame);
        if (0 == pPicHeader->progressive_frame)
        {
            PutBit(pCtx, pPicHeader->picture_structure);
            WRITE_TRACE_LOG(_enc_hdr, "picture_structure", pPicHeader->picture_structure);
        }
        PutBit(pCtx, pPicHeader->top_field_first);
        WRITE_TRACE_LOG(_enc_hdr, "top_field_first", pPicHeader->top_field_first);
        PutBit(pCtx, pPicHeader->repeat_first_field);
        WRITE_TRACE_LOG(_enc_hdr, "repeat_first_field", pPicHeader->repeat_first_field);
        PutBit(pCtx, pPicHeader->fixed_picture_qp);
        WRITE_TRACE_LOG(_enc_hdr, "fixed_picture_qp", pPicHeader->fixed_picture_qp);
        PutBits(pCtx, pPicHeader->picture_qp, 6);
        WRITE_TRACE_LOG(_enc_hdr, "picture_qp", pPicHeader->picture_qp);
        if ((0 == pPicHeader->progressive_frame) &&
            (0 == pPicHeader->picture_structure))
        {
            PutBit(pCtx, pPicHeader->skip_mode_flag);
            WRITE_TRACE_LOG(_enc_hdr, "skip_mode_flag", pPicHeader->skip_mode_flag);
        }
        PutBits(pCtx, (Ipp32u) -1, 4);
        PutBit(pCtx, pPicHeader->loop_filter_disable);
        WRITE_TRACE_LOG(_enc_hdr, "loop_filter_disable", pPicHeader->loop_filter_disable);
        if (0 == pPicHeader->loop_filter_disable)
        {
            PutBit(pCtx, pPicHeader->loop_filter_parameter_flag);
            WRITE_TRACE_LOG(_enc_hdr, "loop_filter_parameter_flag", pPicHeader->loop_filter_parameter_flag);
            if (pPicHeader->loop_filter_parameter_flag)
            {
                PutSE(pCtx, pPicHeader->alpha_c_offset);
                WRITE_TRACE_LOG(_enc_hdr, "alpha_c_offset", pPicHeader->alpha_c_offset);
                PutSE(pCtx, pPicHeader->beta_offset);
                WRITE_TRACE_LOG(_enc_hdr, "beta_offset", pPicHeader->beta_offset);
            }
        }
    }
    // save information about P,B frame
    else
    {
        PutBits(pCtx, pPicHeader->bbv_delay, 16);
        WRITE_TRACE_LOG(_enc_hdr, "bbv_delay", pPicHeader->bbv_delay);
        PutBits(pCtx, pPicHeader->picture_coding_type, 2);
        WRITE_TRACE_LOG(_enc_hdr, "picture_coding_type", pPicHeader->picture_coding_type);
        //WRITE_TRACE_LOG(_enc_hdr, "PictureType", pPicHeader->PictureType);
        PutBits(pCtx, pPicHeader->picture_distance, 8);
        WRITE_TRACE_LOG(_enc_hdr, "picture_distance", pPicHeader->picture_distance);
        if (pSeqHeader->low_delay)
        {
            PutUE(pCtx, pPicHeader->bbv_check_times);
            WRITE_TRACE_LOG(_enc_hdr, "bbv_check_times", pPicHeader->bbv_check_times);
        }

        PutBit(pCtx, pPicHeader->progressive_frame);
        WRITE_TRACE_LOG(_enc_hdr, "progressive_frame", pPicHeader->progressive_frame);

        if (0 == pPicHeader->progressive_frame)
        {
            PutBit(pCtx, pPicHeader->picture_structure);
            WRITE_TRACE_LOG(_enc_hdr, "picture_structure", pPicHeader->picture_structure);
            if (0 == pPicHeader->picture_structure)
            {
                PutBit(pCtx, pPicHeader->advanced_pred_mode_disable);
                WRITE_TRACE_LOG(_enc_hdr, "advanced_pred_mode_disable", pPicHeader->advanced_pred_mode_disable);
            }
        }
        PutBit(pCtx, pPicHeader->top_field_first);
        WRITE_TRACE_LOG(_enc_hdr, "top_field_first", pPicHeader->top_field_first);
        PutBit(pCtx, pPicHeader->repeat_first_field);
        WRITE_TRACE_LOG(_enc_hdr, "repeat_first_field", pPicHeader->repeat_first_field);
        PutBit(pCtx, pPicHeader->fixed_picture_qp);
        WRITE_TRACE_LOG(_enc_hdr, "fixed_picture_qp", pPicHeader->fixed_picture_qp);
        PutBits(pCtx, pPicHeader->picture_qp, 6);
        WRITE_TRACE_LOG(_enc_hdr, "picture_qp", pPicHeader->picture_qp);
        WRITE_TRACE_LOG(_enc_hdr, "picture_structure", pPicHeader->picture_structure);

        if ((AVS_P_PICTURE == pPicHeader->PictureType) ||
            (0 == pPicHeader->picture_structure))
        {
            PutBit(pCtx, pPicHeader->picture_reference_flag);
            WRITE_TRACE_LOG(_enc_hdr, "picture_reference_flag", pPicHeader->picture_reference_flag);
        }
        PutBit(pCtx, pPicHeader->no_forward_reference_flag);
        WRITE_TRACE_LOG(_enc_hdr, "no_forward_reference_flag", pPicHeader->no_forward_reference_flag);
        PutBits(pCtx, (Ipp32u) -1, 3);
        PutBit(pCtx, pPicHeader->skip_mode_flag);
        WRITE_TRACE_LOG(_enc_hdr, "skip_mode_flag", pPicHeader->skip_mode_flag);
        PutBit(pCtx, pPicHeader->loop_filter_disable);
        WRITE_TRACE_LOG(_enc_hdr, "loop_filter_disable", pPicHeader->loop_filter_disable);

        if (0 == pPicHeader->loop_filter_disable)
        {
            PutBit(pCtx, pPicHeader->loop_filter_parameter_flag);
            WRITE_TRACE_LOG(_enc_hdr, "loop_filter_parameter_flag", pPicHeader->loop_filter_parameter_flag);
            if (pPicHeader->loop_filter_parameter_flag)
            {
                PutSE(pCtx, pPicHeader->alpha_c_offset);
                WRITE_TRACE_LOG(_enc_hdr, "alpha_c_offset", pPicHeader->alpha_c_offset);
                PutSE(pCtx, pPicHeader->beta_offset);
                WRITE_TRACE_LOG(_enc_hdr, "beta_offset", pPicHeader->beta_offset);
            }
        }
    }

} // void EncodePicHeader(AVS_BIT_STREAM_CONTEXT *pCtx,

void EncodeSlcHeader(AVS_BIT_STREAM_CONTEXT *pCtx,
                     const AVS_SEQUENCE_HEADER *pSeqHeader,
                     const AVS_PICTURE_HEADER *pPicHeader,
                     const AVS_SLICE_HEADER *pSlcHeader)
{
    // put the start code
    PutBits(pCtx, 1, 24);

    // save information
    PutBits(pCtx, pSlcHeader->slice_vertical_position, 8);
    WRITE_TRACE_LOG(_slc_enc, "slice_vertical_position", pSlcHeader->slice_vertical_position);
    if (2800 < pSeqHeader->vertical_size)
    {
        PutBits(pCtx, pSlcHeader->slice_vertical_position_extension, 3);
        WRITE_TRACE_LOG(_slc_enc, "slice_vertical_position_extension", pSlcHeader->slice_vertical_position_extension);
    }
    if (0 == pPicHeader->fixed_picture_qp)
    {
        PutBit(pCtx, pSlcHeader->fixed_slice_qp);
        WRITE_TRACE_LOG(_slc_enc, "fixed_slice_qp", pSlcHeader->fixed_slice_qp);
        PutBits(pCtx, pSlcHeader->slice_qp, 6);
        WRITE_TRACE_LOG(_slc_enc, "slice_qp", pSlcHeader->slice_qp);
    }

    if ((AVS_I_PICTURE != pPicHeader->PictureType) ||
        (pSlcHeader->isSecondField))
    {
        PutBit(pCtx, pSlcHeader->slice_weighting_flag);
        //WRITE_TRACE_LOG(_slc_enc, "slice_qp", pSlcHeader->slice_weighting_flag);
        WRITE_TRACE_LOG(_slc_enc, "slice_weighting_flag", pSlcHeader->slice_weighting_flag);
        if (pSlcHeader->slice_weighting_flag)
        {
            Ipp32s idx, NumberOfReferenceFwd, NumberOfReferenceBwd;

            // calculate number of references
            NumberOfReferenceFwd = 0;
            NumberOfReferenceBwd = 0;
            // second field of I frame is always P field
            if (AVS_I_PICTURE == pPicHeader->PictureType)
            {
                NumberOfReferenceFwd = 1;
            }
            else if (AVS_P_PICTURE == pPicHeader->PictureType)
            {
                if (pPicHeader->picture_structure)
                    NumberOfReferenceFwd = 2;
                else
                    NumberOfReferenceFwd = 4;
            }
            else
            {
                if (pPicHeader->picture_structure)
                {
                    NumberOfReferenceFwd = 1;
                    NumberOfReferenceBwd = 1;
                }
                else
                {
                    NumberOfReferenceFwd = 2;
                    NumberOfReferenceBwd = 2;
                }
            }

            // forward and backward weighted prediction parameters
            // go in alternate order. That is why following code is
            // sligntly ugly.
            idx = 0;
            while (NumberOfReferenceFwd + NumberOfReferenceBwd)
            {
                if (NumberOfReferenceFwd)
                {
                    NumberOfReferenceFwd -= 1;

                    // read forward weighting parameters
                    PutBits(pCtx, pSlcHeader->luma_scale[AVS_FORWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "luma_scale", pSlcHeader->luma_scale[AVS_FORWARD][idx]);
                    PutSignedBits(pCtx, pSlcHeader->luma_shift[AVS_FORWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "luma_shift", pSlcHeader->luma_shift[AVS_FORWARD][idx]);
                    PutBit(pCtx, 1);
                    PutBits(pCtx, pSlcHeader->chroma_scale[AVS_FORWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "chroma_scale", pSlcHeader->chroma_scale[AVS_FORWARD][idx]);
                    PutSignedBits(pCtx, pSlcHeader->chroma_shift[AVS_FORWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "chroma_shift", pSlcHeader->chroma_shift[AVS_FORWARD][idx]);
                    PutBit(pCtx, 1);
                }

                if (NumberOfReferenceBwd)
                {
                    NumberOfReferenceBwd -= 1;

                    // read backward weighting parameters
                    PutBits(pCtx, pSlcHeader->luma_scale[AVS_BACKWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "luma_scale", pSlcHeader->luma_scale[AVS_BACKWARD][idx]);
                    PutSignedBits(pCtx, pSlcHeader->luma_shift[AVS_BACKWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "luma_shift", pSlcHeader->luma_shift[AVS_BACKWARD][idx]);
                    PutBit(pCtx, 1);
                    PutBits(pCtx, pSlcHeader->chroma_scale[AVS_BACKWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "chroma_scale", pSlcHeader->chroma_scale[AVS_BACKWARD][idx]);
                    PutSignedBits(pCtx, pSlcHeader->chroma_shift[AVS_BACKWARD][idx], 8);
                    WRITE_TRACE_LOG(_slc_enc, "chroma_shift", pSlcHeader->chroma_shift[AVS_BACKWARD][idx]);
                    PutBit(pCtx, 1);
                }

                // increment writing index
                idx += 1;
            }
            PutBit(pCtx, pSlcHeader->mb_weighting_flag);
            WRITE_TRACE_LOG(_slc_enc, "mb_weighting_flag", pSlcHeader->mb_weighting_flag);
        }
    }

} // void DecodeSlcHeader(AVS_BIT_STREAM_CONTEXT *pCtx,
/*
Ipp32s GetFieldNum(Ipp32u *pSlcData,
                   AVS_SEQUENCE_HEADER *pSeqHeader,
                   AVS_PICTURE_HEADER *pPicHeader)
{
    AVS_BIT_STREAM_CONTEXT ctx;
    Ipp32u slice_vertical_position;
    Ipp32u slice_vertical_position_extension;
    Ipp32u MbHeight, MbRow;

    // this is a frame, there are no fields
    if (pPicHeader->picture_structure)
        return 0;

    ctx.src = pSlcData;
    ctx.offset = 31;

    // read the start code
    SkipBits(&ctx, 24);

    // read information
    slice_vertical_position = PutBits(&ctx, 8);
    if (2800 < pSeqHeader->vertical_size)
        slice_vertical_position_extension = PutBits(&ctx, 3);
    else
        slice_vertical_position_extension = 0;

    // calculate frame dimensions
    if (pPicHeader->picture_structure)
        MbHeight = ((pSeqHeader->vertical_size + 15) / 16);
    else
        MbHeight = 2 * ((pSeqHeader->vertical_size + 31) / 32);

    // calculate the slice position
    MbRow = slice_vertical_position;
    if (2800 < pSeqHeader->vertical_size)
        MbRow += (slice_vertical_position_extension << 7);

    if (MbHeight / 2 <= MbRow)
        return 1;
    else
        return 0;

} // Ipp32s GetFieldNum(Ipp32u *pSlcData,
*/
} // namespace UMC

#endif // defined(UMC_ENABLE_AVS_VIDEO_ENCODER)
